Vue 默認按照“就地更新”的策略來更新通過 v-for 渲染的元素列表。當數據項的順序改變時,Vue 不會隨之移動 DOM 元素的順序,而是就地更新每個元素,確保它們在原本指定的索引位置上渲染。
默認模式是高效的,但只適用於列表渲染輸出的結果不依賴子組件狀態或者臨時 DOM 狀態 (例如表單輸入值) 的情況。
為了給 Vue 一個提示,以便它可以跟蹤每個節點的標識,從而重用和重新排序現有的元素,你需要為每個元素對應的塊提供一個唯一的 key attribute:
<div v-for="item in items" :key="item.id">
<!-- 內容 -->
</div>
當你使用 template v-for 時,key 應該被放置在這個 template 容器上:
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
注意
key 在這裡是一個通過 v-bind 綁定的特殊 attribute。請不要和在 v-for 中使用對象裡所提到的對象屬性名相混淆。
推薦在任何可行的時候為 v-for 提供一個 key attribute,除非所迭代的 DOM 內容非常簡單 (例如:不包含組件或有狀態的 DOM 元素),或者你想有意採用默認行為來提高性能。
key 綁定的值期望是一個基礎類型的值,例如字符串或 number 類型。不要用對象作為 v-for 的 key。
我們可以直接在組件上使用 v-for,和在一般的元素上使用沒有區別 (別忘記提供一個 key):
<MyComponent v-for="item in items" :key="item.id" />
但是,這不會自動將任何數據傳遞給組件,因為組件有自己獨立的作用域。為了將迭代後的數據傳遞到組件中,我們還需要傳遞 props:
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
不自動將 item 注入組件的原因是,這會使組件與 v-for 的工作方式緊密耦合。明確其數據的來源可以使組件在其他情況下重用。
Vue 能夠偵聽響應式數組的變更方法,並在它們被調用時觸發相關的更新。這些變更方法包括:
變更方法,顧名思義,就是會對調用它們的原數組進行變更。相對地,也有一些不可變 (immutable) 方法,例如 filter(),concat() 和 slice(),這些都不會更改原數組,而總是返回一個新數組。當遇到的是非變更方法時,我們需要將舊的數組替換為新的:
// `items` 是一個數組的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
你可能認為這將導致 Vue 丟棄現有的 DOM 並重新渲染整個列表——幸運的是,情況並非如此。Vue 實現了一些巧妙的方法來最大化對 DOM 元素的重用,因此用另一個包含部分重疊對象的數組來做替換,仍會是一種非常高效的操作。
有時,我們希望顯示數組經過過濾或排序後的內容,而不實際變更或重置原始數據。在這種情況下,你可以創建返回已過濾或已排序數組的計算屬性。
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((n) => n % 2 === 0)
})
<li v-for="n in evenNumbers">{{ n }}</li>
在計算屬性不可行的情況下 (例如在多層嵌套的 v-for 循環中),你可以使用以下方法:
const sets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
function even(numbers) {
return numbers.filter((number) => number % 2 === 0)
}
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
在計算屬性中使用 reverse() 和 sort() 的時候務必小心!這兩個方法將變更原始數組,計算函數中不應該這麼做。請在調用這些方法之前創建一個原數組的副本:
- return numbers.reverse()
+ return [...numbers].reverse()
key 這個特殊的屬性主要作為 Vue 的虛擬 DOM 演算法提示,用於在比較新舊節點列表時識別 vnode。
預期:number | string | symbol
在沒有 key 的情況下,Vue 會使用一種最小化元素移動的演算法,並儘可能就地更新/複用相同類型的元素。如果傳遞了 key,則會根據 key 的變化順序重新排列元素,並且會始終移除/銷毀 key 已經不存在的元素。
同一個父元素下的子元素必須具有唯一的 key。重複的 key 會導致渲染異常。
也可以用於強制替換一個元素/組件而不是複用它。當你想這麼做時,它可能會很有用:
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text 变化时,span 总是会被替换而不是更新,因此 transition 将会被触发。